package no.uio.inf5750.j2mewsclient;

import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import no.uio.inf5750.wsclient.dao.DataValueDAO;
import no.uio.inf5750.wsclient.dao.DatasetDAO;
import no.uio.inf5750.wsclient.dao.OrganisationUnitDAO;
import no.uio.inf5750.wsclient.dao.PeriodDAO;
import no.uio.inf5750.wsclient.dao.mockup.MockupDataValueDAO;
import no.uio.inf5750.wsclient.dao.mockup.MockupDatasetDAO;
import no.uio.inf5750.wsclient.dao.mockup.MockupOrganisationUnitDAO;
import no.uio.inf5750.wsclient.dao.mockup.MockupPeriodDAO;
import no.uio.inf5750.wsclient.model.DataElement;
import no.uio.inf5750.wsclient.model.DataValue;
import no.uio.inf5750.wsclient.model.Dataset;
import no.uio.inf5750.wsclient.model.OrganisationUnit;
import no.uio.inf5750.wsclient.model.Period;

public class SimpleMIDlet
    extends MIDlet
    implements CommandListener
{

    private Display display;

    private ChoiceGroup choiceGroup;

    private Form welcomeScreen;

    private List selectOrgUnitScreen;

    private List selectDatasetScreen;

    private List selectPeriodScreen;

    private Form editDatasetScreen;

    private Form saveDoneScreen;

    private Command cmdOk;

    private Command cmdSave;

    private Command cmdBack;

    private Command cmdExit;

    private Command cmdSelect;

    private OrganisationUnitDAO orgUnitDAO;

    private DatasetDAO datasetDAO;

    private PeriodDAO periodDAO;

    private DataValueDAO dataValueDAO;

    private Vector orgUnits;

    private Vector datasets;

    private Vector periods;

    private Vector fields;

    private OrganisationUnit selectedOrgUnit;

    private Dataset selectedDataset;

    private Period selectedPeriod;

    public void startApp()
        throws MIDletStateChangeException
    {

        display = Display.getDisplay( this );

        orgUnitDAO = new MockupOrganisationUnitDAO();
        datasetDAO = new MockupDatasetDAO();
        periodDAO = new MockupPeriodDAO();
        dataValueDAO = new MockupDataValueDAO();

        cmdOk = new Command( "Ok", Command.OK, 0 );
        cmdSave = new Command( "Save", Command.OK, 0 );
        cmdBack = new Command( "Back", Command.BACK, 1 );
        cmdExit = new Command( "Exit", Command.EXIT, 2 );
        cmdSelect = new Command( "Select", Command.ITEM, 1 );

        welcomeScreen = createWelcomeScreen();

        /*
         * Runtime rt = Runtime.getRuntime(); welcomeForm.append("Free Memory: "
         * + rt.freeMemory()); welcomeForm.append("Total Memory: " +
         * rt.totalMemory());
         */

        // welcomeForm.append(dhisSystem.getOrganisationUnit(1).getName());
        System.out.println( "Starting app" );

        display.setCurrent( welcomeScreen );
    }

    public void pauseApp()
    {

    }

    public void destroyApp( boolean unconditional )
        throws MIDletStateChangeException
    {
        notifyDestroyed();
    }

    public void commandAction( Command c, Displayable d )
    {

        System.out.println( "Command " + c.getLabel() );
        if ( c == cmdExit )
        {
            try
            {
                destroyApp( true );
            }
            catch ( MIDletStateChangeException e )
            {
                showException( e );
            }
            return;
        }

        if ( display.getCurrent() == welcomeScreen )
        {
            if ( c == cmdOk )
            {
                String selected = choiceGroup.getString( choiceGroup.getSelectedIndex() );
                System.out.println( selected );
                if ( selected.toLowerCase().indexOf( "data" ) > -1 && selected.toLowerCase().indexOf( "entry" ) > -1 )
                {
                    selectOrgUnitScreen = createSelectOrgUnitScreen();
                    display.setCurrent( selectOrgUnitScreen );
                }
                else if ( selected.toLowerCase().indexOf( "simplified" ) > -1 )
                {
                    editDatasetScreen = createEditDatasetScreen();
                    display.setCurrent( editDatasetScreen );
                }
            }

        }
        else if ( display.getCurrent() == selectOrgUnitScreen )
        {
            if ( c == cmdSelect )
            {
                int selected = selectOrgUnitScreen.getSelectedIndex();
                selectedOrgUnit = (OrganisationUnit) orgUnits.elementAt( selected );
                System.out.println( selectedOrgUnit.getName() );

                selectDatasetScreen = createSelectDatasetScreen();
                display.setCurrent( selectDatasetScreen );

            }
            else if ( c == cmdBack )
            {
                display.setCurrent( welcomeScreen );
            }

        }
        else if ( display.getCurrent() == selectDatasetScreen )
        {
            if ( c == cmdSelect )
            {

                int selected = selectDatasetScreen.getSelectedIndex();
                selectedDataset = (Dataset) datasets.elementAt( selected );
                System.out.println( selectedOrgUnit.getName() );

                selectPeriodScreen = createSelectPeriodScreen();
                display.setCurrent( selectPeriodScreen );

            }
            else if ( c == cmdBack )
            {
                display.setCurrent( selectOrgUnitScreen );
            }

        }
        else if ( display.getCurrent() == selectPeriodScreen )
        {
            if ( c == cmdSelect )
            {

                int selected = selectPeriodScreen.getSelectedIndex();
                selectedPeriod = (Period) periods.elementAt( selected );
                System.out.println( selectedPeriod.getName() );

                editDatasetScreen = createEditDatasetScreen();
                display.setCurrent( editDatasetScreen );

            }
            else if ( c == cmdBack )
            {
                display.setCurrent( selectDatasetScreen );
            }

        }
        else if ( display.getCurrent() == editDatasetScreen )
        {
            if ( c == cmdSave )
            {

                for ( int i = 0; i < fields.size(); ++i )
                {
                    Field field = (Field) fields.elementAt( i );

                    DataElement dataElement = field.getDataElement();
                    DataValue dataValue = field.getDataValue();
                    String original = field.getOriginalValue();
                    Item item = field.getItem();

                    // Get value from formitem
                    String value = "";
                    if ( item.getClass() == TextField.class )
                    {
                        value = ((TextField) item).getString();
                    }
                    else if ( item.getClass() == ChoiceGroup.class )
                    {
                        value = ((ChoiceGroup) item).isSelected( 0 ) ? "1" : "0";
                    }

                    // If a value already exists
                    if ( dataValue != null && !field.isNew() )
                    {
                        // If this value has been updated
                        if ( !original.equals( value ) )
                        {
                            System.out.println( "Value for element " + dataElement.getName() + " is modified." );
                            dataValue.setValue( value );

                            field.setUpdated( true );
                            saveField( field );
                        }
                        else
                        {
                            // Remove any annotations from previous succsessful
                            // save
                            field.setStatus( Field.UNCHANGED );
                            System.out.println( "Value for element " + dataElement.getName() + " is unchanged." );
                        }
                    }
                    else
                    {
                        // If this is a new value
                        if ( !value.equals( "" ) )
                        {
                            System.out.println( "Value for element " + dataElement.getName() + " is new." );

                            dataValue = new DataValue();
                            dataValue.setOrgUnit( selectedOrgUnit );
                            dataValue.setPeriod( selectedPeriod );
                            dataValue.setDataElement( dataElement );
                            dataValue.setValue( value );

                            field.setDataValue( dataValue );
                            field.setNew( true );
                            saveField( field );
                        }
                        else
                        {
                            System.out.println( "Value for element " + dataElement.getName() + " is unchanged." );
                        }
                    }
                }

                saveDoneScreen = createSaveDoneScreen();
                display.setCurrent( saveDoneScreen );

            }
            else if ( c == cmdBack )
            {
                display.setCurrent( selectPeriodScreen );
            }

        }
        else if ( display.getCurrent() == saveDoneScreen )
        {
            if ( c == cmdBack )
            {
                display.setCurrent( editDatasetScreen );
            }

        }
    }

    public void saveField( Field field )
    {

        /*
         * TODO: Should do the following in a seperate thread to avoid locking
         * up the device.
         */

        if ( field.isNew() )
        {
            // Save new value
            try
            {
                dataValueDAO.newDataValue( field.getDataValue() );

                // If save successful
                field.setNew( false );
                field.setOriginalValue( field.getDataValue().getValue() );
                field.setStatus( Field.SAVED );

            }
            catch ( Exception e )
            {
                // If save failed
                field.setStatus( Field.SAVEFAILED );
            }
        }
        else if ( field.isUpdated() )
        {
            // Update modified value
            try
            {
                dataValueDAO.updateDataValue( field.getDataValue() );

                // If save successful
                field.setUpdated( false );
                field.setOriginalValue( field.getDataValue().getValue() );
                field.setStatus( Field.SAVED );

            }
            catch ( Exception e )
            {
                // If save failed
                field.setStatus( Field.SAVEFAILED );
            }
        }
    }

    public void showException( Exception e )
    {
        Alert alert = new Alert( "Error" );
        alert.setString( e.getMessage() );
        alert.setType( AlertType.ERROR );
        alert.setTimeout( Alert.FOREVER );
        display.setCurrent( alert );
    }

    public Form createWelcomeScreen()
    {
        Form form = new Form( "DHIS" );
        form.addCommand( cmdExit );
        form.addCommand( cmdOk );
        form.append( "Welcome to the DHIS mobile client.\n" );
        String[] elements = { "Data entry", "Simplified add/modify data for dataset (Spike)" };
        choiceGroup = new ChoiceGroup( "Please select an action from the list below", ChoiceGroup.EXCLUSIVE, elements,
            null );
        form.append( choiceGroup );

        form.setCommandListener( this );

        return form;
    }

    public List createSelectOrgUnitScreen()
    {

        orgUnits = orgUnitDAO.listOrganisationUnits( null );

        List list = new List( "Select organisation unit", List.IMPLICIT );

        for ( Enumeration e = orgUnits.elements(); e.hasMoreElements(); )
        {
            list.append( ((OrganisationUnit) e.nextElement()).getName(), null );
        }

        list.addCommand( cmdSelect );
        list.addCommand( cmdBack );
        list.setCommandListener( this );

        return list;
    }

    public List createSelectDatasetScreen()
    {
        datasets = datasetDAO.listDatasets( selectedOrgUnit );
        List list = new List( "Available datasets", List.IMPLICIT );
        for ( Enumeration e = datasets.elements(); e.hasMoreElements(); )
        {
            list.append( ((Dataset) e.nextElement()).getName(), null );
        }

        list.setSelectCommand( cmdSelect );
        list.addCommand( cmdBack );
        list.setCommandListener( this );

        return list;
    }

    public List createSelectPeriodScreen()
    {
        periods = periodDAO.listPeriods( selectedOrgUnit, selectedDataset );
        List list = new List( "Available periods", List.IMPLICIT );

        for ( Enumeration e = periods.elements(); e.hasMoreElements(); )
        {
            list.append( ((Period) e.nextElement()).getName(), null );
        }

        list.setSelectCommand( cmdSelect );
        list.addCommand( cmdBack );
        list.setCommandListener( this );

        return list;
    }

    public Form createEditDatasetScreen()
    {
        Form form = new Form( "Update dataset" );

        fields = new Vector();

        Dataset dataset = datasetDAO.getDataset( selectedOrgUnit, selectedPeriod, selectedDataset );
        Vector elements = dataset.getDataElements();
        Vector values = dataset.getDataValues();

        for ( Enumeration e = elements.elements(); e.hasMoreElements(); )
        {
            DataElement dataElement = (DataElement) e.nextElement();
            Field field = new Field();

            field.setDataElement( dataElement );

            // Find datavalue for dataelement if there is one
            DataValue dataValue = null;
            for ( Enumeration e2 = values.elements(); e2.hasMoreElements(); )
            {
                dataValue = (DataValue) e2.nextElement();
                if ( dataValue.getDataElement() == dataElement )
                {
                    field.setDataValue( dataValue );
                    field.setOriginalValue( dataValue.getValue() );
                    break;
                }
                else
                {
                    dataValue = null;
                }

            }

            // Create formitems and set values when there are any
            String value = (dataValue != null) ? dataValue.getValue() : "";
            Item item;
            if ( dataElement.getType().equalsIgnoreCase( "string" ) )
            {
                item = new TextField( dataElement.getName(), value, 100, TextField.ANY );

            }
            else if ( dataElement.getType().equalsIgnoreCase( "int" ) )
            {
                item = new TextField( dataElement.getName(), value, 100, TextField.NUMERIC );

            }
            else if ( dataElement.getType().equalsIgnoreCase( "bool" ) )
            {
                ChoiceGroup cg = new ChoiceGroup( "", ChoiceGroup.MULTIPLE );
                cg.append( dataElement.getName(), null );
                boolean flags[] = { value.equals( "1" ) };
                cg.setSelectedFlags( flags );
                item = cg;

            }
            else
            {
                item = new StringItem( dataElement.getName(), "Unsupported datatype." );
            }

            field.setItem( item );
            form.append( item );

            fields.addElement( field );
        }

        form.addCommand( cmdSave );
        form.addCommand( cmdBack );

        form.setCommandListener( this );

        return form;
    }

    public Form createSaveDoneScreen()
    {
        Form form = new Form( "Save done" );

        /*
         * TODO: Indicate progress and results of saving changes since each
         * element is saved seperately.
         */
        form.append( "Data \"saved\"." );
        form.addCommand( cmdBack );

        form.setCommandListener( this );

        return form;
    }
}
